package org.openapi.service.impl;

import org.openapi.common.ApiException;
import org.openapi.common.ApiResult;
import org.openapi.common.ApiUser;
import org.openapi.consts.SqlType;
import org.openapi.domain.ApiAccess;
import org.openapi.domain.ApiModel;
import org.openapi.domain.ApiSql;
import org.openapi.parser.DataParser;
import org.openapi.parser.QueryParser;
import org.openapi.service.IApiLoaderService;
import org.openapi.service.IDbService;
import org.openapi.service.IOpenApiService;
import org.openapi.service.IOpenDataService;
import org.openapi.utils.JsUtil;
import org.openapi.utils.StrUtil;
import org.openapi.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Service
@Slf4j
public class OpenDataServiceImpl implements IOpenDataService {
    @Autowired
    private IApiLoaderService apiLoaderService;
    @Autowired
    private IDbService dbService;
    @Autowired
    private IOpenApiService openApiService;

    @Override
    public ApiResult run(HttpServletRequest request, String body) {
        String method = request.getMethod().toUpperCase();
        ApiResult ret = null;
        switch (method){
            case "GET":
                ret =  ApiResult.success(doGet(request.getQueryString()));
                break;
            case "DELETE":
                ret = ApiResult.success(doDelete(request.getQueryString()));
                break;
            case "POST":
                ret = ApiResult.success(doPost(body));
                break;
            case "PUT":
                ret = ApiResult.success(doPut(body));
                break;
            case "PATCH":
                ret = ApiResult.success(doPatch(body));
                break;
            default:
                ret = ApiResult.error("不支持方法:" + method);
                break;
        }
        return ret;
    }

    /**
     * 查询
     * @param paramsStr
     * @return
     */
    @Override
    public Map doGet(String paramsStr){
        TableQuery query = QueryParser.parse(paramsStr);
        RelQuery relQuery = null;
        //判断查询模型是否为多表
        if(query instanceof RelQuery) {
            relQuery = (RelQuery) query;
            TableQuery table = relQuery.getMainTable();
            table.setModel(checkModel(table.getTable(), ApiAccess.READ_MODE));
        }else{
            query.setModel(checkModel(query.getTable(), ApiAccess.READ_MODE));
        }

        //分页查询
        if(query.getPager() != null){
            return dbService.page(query);
        }

        //单表单行查询
        if(relQuery == null || relQuery.getSubs().isEmpty()){
            return dbService.find(query);
        }

        //多表单行
        Map retData = new HashMap();
        TableQuery table = relQuery.getMainTable();
        dbService.relData(retData, table.getData());
        retData.put(table.getAlias(), dbService.find(table));

        //再查子表数据
        Iterator<String> it = relQuery.getSubs().keySet().iterator();
        while(it.hasNext()){
            table = relQuery.getSubs().get(it.next());
            table.setModel(checkModel(table.getTable(), ApiAccess.READ_MODE));
            dbService.relData(retData, table.getData());

            if(table.getPager() != null){
                retData.put(table.getAlias(), dbService.query(table));
            }else {
                retData.put(table.getAlias(), dbService.find(table));
            }
        }
        return retData;
    }


    /**
     * 删除
     * @param paramsStr
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int doDelete(String paramsStr){
        int rows = 0;
        List<TableQuery> list = DataParser.parseDelete(paramsStr);
        SqlPager pager = new SqlPager(10000, 1);
        for(TableQuery table:list){
            table.setModel(checkModel(table.getTable(), ApiAccess.WRITE_MODE));
            table.setPager(pager);
            rows += dbService.deleteData(table);
        }
        return rows;
    }

    /**
     * 新增数据，如果模型配置了唯一字段，就会唯一字段做查询判断是否有旧数据，如果有旧数据，就认为是重复，报错
     * @param paramsStr
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map doPost(String paramsStr){
        RelData data = DataParser.parseData(paramsStr);
        Map<String, Map> retMap = new HashMap<>();
        for(TableData table: data.getTables()){
            table.setModel(checkModel(table.getTable(), ApiAccess.WRITE_MODE));
            table.setIgnore(data.getIgnore());

            Map<String, Object> dataMap = table.getData();
            dbService.relData(retMap, dataMap);
            dbService.insertData(table, false);
            retMap.put(table.getTable(), dataMap);
        }
        return retMap;
    }

    /**
     * 更新数据，需要有主键数据，没有就报错
     * @param paramsStr
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int doPut(String paramsStr){
        int rows = 0;
        RelData data = DataParser.parseData(paramsStr);
        Map<String, Map> retMap = new HashMap<>();
        for(TableData table: data.getTables()){
            table.setModel(checkModel(table.getTable(), ApiAccess.WRITE_MODE));
            table.setIgnore(data.getIgnore());

            Map<String, Object> dataMap = table.getData();
            dbService.relData(retMap, dataMap);
            rows += dbService.updateData(table);
            retMap.put(table.getTable(), dataMap);
        }
        return rows;
    }


    /**
     * 更新或新增
     * 如果带有主键数据，会查询是否有旧数据：如果有旧数据会自动更新，没有旧数据会新增
     * 如果没有主键数据，但是模型配置了唯一字段，会按唯一字段做查询判断是否有旧数据：有旧数据就更新，没有旧数据就新增
     * @param paramsStr
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int doPatch(String paramsStr){
        int rows = 0;
        RelData data = DataParser.parseData(paramsStr);
        Map<String, Map> retMap = new HashMap<>();
        for(TableData table: data.getTables()){
            table.setModel(checkModel(table.getTable(), ApiAccess.WRITE_MODE));
            table.setIgnore(data.getIgnore());

            Map<String, Object> dataMap = table.getData();
            dbService.relData(retMap, dataMap);
            rows += dbService.insertData(table, true);
            retMap.put(table.getTable(), dataMap);
        }
        return rows;
    }

    /**
     * 查询数据源，支持类型是统计报表，列表查询和计算，不支持子查询和批处理
     * @param dsCode
     * @param params
     * @return
     */
    @Override
    public Object doDs(String dsCode, Map params){
        ApiSql apiSql = apiLoaderService.getSql(dsCode);
        if(apiSql == null){
            throw new ApiException("数据源 " + dsCode +" 不存在");
        }
        //批量操作和子查询不支持直接调用
        if(apiSql.getType().equalsIgnoreCase(SqlType.BATCH.getCode())){
            throw new ApiException("不支持的操作");
        }
        //生成SQL
        String sql = apiSql.isFun() ? JsUtil.exec(apiSql.getContent(), params) : apiSql.getContent();

        //加入数据权限
        if(sql.contains("${userId}") || sql.contains("${orgId}") || sql.contains("${orgIds}")) {
            ApiUser user = openApiService.getLoginUser();
            sql = sql.replaceAll("\\$\\{userId\\}", user.getUserId());
            sql = sql.replaceAll("\\$\\{orgId\\}", user.getOrgId());
            if(sql.contains("${orgIds}")) {
                sql = sql.replaceAll("\\$\\{orgIds\\}", "'" + StrUtil.join(user.getOrgIds(), "','") + "'");
            }
        }

        //列表查询
        if(apiSql.getType().equalsIgnoreCase(SqlType.PAGE.getCode())){
            int page = Integer.parseInt(params.getOrDefault("page", "1").toString());
            int limit = Integer.parseInt(params.getOrDefault("limit", "10").toString());
            String order = params.containsKey("order") ? params.get("order").toString() : null;
            return dbService.page(sql, params, new SqlPager(limit, page, order));
        }

        //非列表查询
        List<Map<String,Object>> list = dbService.query(sql);
        if(list == null || list.isEmpty()){
            return null;
        }
        //计算结果只返回一个值
        if(apiSql.getType().equalsIgnoreCase(SqlType.COUNT.getCode())){
            return list.get(0).values().iterator().next();
        }else{
            //统计报表和列表查询返回全部
            return list;
        }
    }

    /**
     * 查询模型信息并验证权限
     * @param table
     * @param access
     * @return
     */
    private ApiModel checkModel(String table, String access){
        ApiModel model = apiLoaderService.getModel(table);
        if(model == null){
            throw new ApiException("数据模型未定义:" + table);
        }

        //验证权限
        if(!apiLoaderService.checkAccess(model, ApiAccess.WRITE_MODE)){
            throw new ApiException("您没有访问权限");
        }

        return model;
    }
}
